package eu.jk.config;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import eu.jk.helper.ResourceUtil;

/**
 * 配置段类
 * 
 * @author wenjianwzz
 */
final class Config {

	/**
	 * 配置项
	 */
	private Map<String, ConfigItem> items = new HashMap<String, ConfigItem>();

	/**
	 * 子配置段
	 */
	private Map<String, Config> subconfigs = new HashMap<String, Config>();

	/**
	 * 父亲
	 */
	private Config parent = null;

	/**
	 * 配置段名
	 */
	private String name = null;

	/**
	 * 取得键对应的值,如果不存在对应的值, 返回NULL
	 * 
	 * @param key
	 * @return String
	 * @see java.util.hashtable#get(Object)
	 */
	public String getValue(String key) {
		return items.get(key).getValue();
	}

	/**
	 * 取得键对应的值,如果不存在对应的值, 返回NULL
	 * 
	 * @param key
	 * @return String
	 * @see java.util.hashtable#get(Object)
	 */
	public String getValue(String key, int index) {
		return items.get(key).getValue(index);
	}
	
	/**
	 * 查找配置项
	 * @param key
	 * @return
	 */
	public ConfigItem getConfigItem(String key) {
		return items.get(key);
	}
	/**
	 * 是否包含指定键的配置项
	 * @param key
	 * @return
	 */
	public boolean contains(String key) {
		return items.containsKey(key);
	}

	/**
	 * 获取所有的配置项名称
	 * @return
	 */
	public Collection<String> getItemsKeys() {
		return items.keySet();
	}

	/**
	 * 获取配置段名
	 */
	public String getName() {
		return name;
	}

	/**
	 * 获取所有子配置段的名称
	 * @return
	 */
	public Collection<String> getSubconfigKeys() {
		return subconfigs.keySet();
	}

	/**
	 * 获取子配置段
	 * @param name
	 * @return
	 */
	public Config getSubconfig(String name) {
		return subconfigs.get(name);
	}

	/**
	 * 是否包含指定名称的子配置段
	 * @param name
	 * @return
	 */
	public boolean containsSubconfig(String name) {
		return subconfigs.containsKey(name);
	}

	/**
	 * 加载配置项
	 * @param id
	 * @return
	 */
	public ConfigItem locateItem(String id) {
		return (ConfigItem) locate(id);
	}

	/**
	 * 加载配置值
	 * @param id
	 * @return
	 */
	public String locateValue(String id) {
		Object object = locate(id);
		if (object == null)
			return null;
		if (object instanceof ConfigItem)
			return ((ConfigItem)object).getValue();
		if (object instanceof String)
			return (String) object;
		return null;
	}

	/**
	 * 加载配置项
	 * @param id
	 * @return
	 */
	public Config locateConfig(String id) {
		return (Config) locate(id);
	}

	/**
	 * 加载
	 * @param id
	 * @return
	 */
	protected Object locate(String id) {
		if (id == null)
			return null;
		// 定位基点
		Config base = this;
		if (id.startsWith("/"))
			while (base.getParent() != null)
				base = base.getParent();
		String[] fields = id.split("/");
		for (String field : fields) {
			field = field.trim();
			if ("".equals(field))
				continue;
			if (field.startsWith("@")) {
				if (field.matches("^@.+\\[[0-9]+\\]$")) {
					String key = field.substring(1, field.indexOf('['));
					String index = field.substring(field.indexOf('[') + 1, field.indexOf(']'));
					if (!isDigit(index))
						return null;
					return base.getConfigItem(key).getValue(Integer.parseInt(index));
				}
				return base.getConfigItem(field.substring(1));
			}
			if ("..".equals(field))
				base = base.getParent();
			else
				base = base.getSubconfig(field);
			if (base == null)
				return null;
		}
		return base;
	}

	/**
	 * 添加参数, 如果原来存在参数, 会接在后面成为一个数组
	 * 
	 * @param key
	 * @param value
	 */
	protected void addParameters(String key, String value) {
		ConfigItem item = items.get(key);
		if (item == null)
			items.put(key, new ConfigItem(key, value));
		else
			item.addValue(value);
	}

	protected void setConfigName(String configName) {
		this.name = configName;
	}

	protected Config(String configName) {
		this.name = configName;
	}

	/**
	 * 添加子配置段
	 * @param subconfig
	 */
	protected void addSubconfig(Config subconfig) {
		subconfig.setConfigName(subconfig.getName().trim());
		subconfigs.put(subconfig.getName(), subconfig);
	}

	protected void setParent(Config parent) {
		this.parent = parent;
	}

	protected Config getParent() {
		return parent;
	}

	/**
	 * 加载配置文件, 返回根配置段
	 * @param path 配置文件的路径
	 * @param encoding 文件编码, 如果为null则使用系统默认编码
	 * @return 根配置
	 */
	public static Config load(String path, String encoding) {
		try {
			return load(new FileInputStream(path), encoding, path);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 使用系统默认编码加载配置文件, 返回根配置
	 * @param path 配置文件的路径
	 * @return 根配置
	 */
	public static Config load(String path) {
		return load(path, null);
	}
	
	/**
	 * 从流中加载配置, 使用系统默认编码
	 * @param input 输入流
	 * @param rootConfigName 根配置名
	 * @return
	 */
	public static Config load(InputStream input, String rootConfigName) {
		return load(input, null, rootConfigName);
	}
	
	/**
	 * 从流中加载配置, 使用系统默认编码
	 * @param input 输入流
	 * @return 根配置
	 */
	public static Config load(InputStream input) {
		return load(input, null, "anonymous");
	}
	
	public static Config load(InputStream input, String encoding, String rootConfigName) {
		InputStreamReader fr;
		try {
			if (encoding != null)
				fr = new InputStreamReader(input, encoding);
			else 
				fr = new InputStreamReader(input);
			BufferedReader br = new BufferedReader(fr);
			return load(br, rootConfigName);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * 从流中加载配置, 使用指定编码
	 * @param input 输入流
	 * @param encoding 编码
	 * @param rootConfigName 根配置名
	 * @return 根配置
	 */
	public static Config load(Reader reader, String rootConfigName) {
		Config root = new Config(rootConfigName);
		for (String line : readLines(reader)) {
			line = line.trim();
			if (line.startsWith("#"))
				continue;
			if (line.length() == 0)
				continue;
			//SubConfig found
			if (line.matches("^.+\\{$")) {
				String name = new String(line.replace('{', '\b').trim());
				Config sub = new Config(name);
				root.addSubconfig(sub);
				sub.setParent(root);
				root = sub;
				continue;
			}
			/*
			 * 往上翻一层
			 */
			if ("}".equals(line)) {
				root = root.getParent();
				continue;
			}
			/*
			 * 键值对的配置
			 */
			if (line.contains(":")) {
				int index = line.indexOf(':');
				String key = line.substring(0, index).trim();
				String value = line.substring(index + 1).trim();
				if (value.matches("^.+,.+$")) {
					for (String sub : value.split(",")) {
						sub = sub.trim();
						if (sub.length() == 0)
							continue;
						root.addParameters(key, sub);
					}
					continue;
				}
				root.addParameters(key, value);
				continue;
			}
			root.addParameters(line, null);
		}
		return root;
	}
	/**
	 * 判断一个字符串是不是数字
	 * @param str
	 * @return boolean
	 */
	private static boolean isDigit(String str) {
		if (str == null)
			return false;
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			if (c < 48 || c > 57)
				return false;
		}
		return true;
	}
	
	
	/**
	 * 读取所有行
	 * @param path
	 * @param encoding
	 * @return
	 */
	private static List<String> readLines(Reader reader) {
		List<String> list = new ArrayList<String>();
		try {
			BufferedReader br = new BufferedReader(reader);
			String line = br.readLine();
			while (line != null) {
				list.add(line);
				line = br.readLine();
			}
			br.close();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		return list;
	}
	
	public static void main(String args[]) {
		//加载配置文件, 返回一个根配置段(文件中所有的)
		Config config = Config.load(ResourceUtil.getResourcePath("new.conf"), "UTF-8");
		//定位配置, @表示查找配置项, 如果不加@则是查找子配置段
		System.out.println(config.locateValue("/system/@whoami"));
		//可以用..上翻
		System.out.println(config.locateValue("/system/../@where"));
		//数组配置的某一值
		System.out.println(config.locateValue("/system/config/@message[0]"));
		//查找子孙配置段, 不用/打头表示从当前配置段定位, 这里因为本来就是根配置, 所以就一样了
		config = config.locateConfig("system/config");
		//从当前配置段定位
		System.out.println(config.locateValue("@message[0]"));
		// 上翻的作用
		System.out.println(config.locateValue("../@whoami"));
	}
}
